OpenFeign 从入门到放弃

作者: 李多多 日期: 2020-08-08
Spring Cloud
OpenFeign 从入门到放弃

@TOC

前言

之前使用的eureka/hystrix 都是调用RestTemplate(繁琐 重复高) 。而OpenFeign对请求进行简化(实际上就是对之前请求的一个封装)。Feign停更了 OpenFeign是在Feign基础上开发出来的。

常用的几种接口调用方法:

  • Httpclient 易用 灵活
  • Okhttp 处理网络请求 轻量级 支持多协议。。
  • HttpURLConnection 使用复杂
  • RestTemplate Rest服务的客户端 提供多种便携访问HTTP服务的方法

1.HelloWorld

还是继续使用之前的 provider 和 eureka配置,重新建一个spring boot项目,创建时选OpenFeign 依赖。如下:
配置 OpenFeign 依赖
创建成功后,在application.properties中添加如下配置:

spring.application.name=openfeign
server.port=5000
eureka.client.service-url.defaultZone=http://localhost:1111/eureka

接下来,在项目启动类上面添加注解,开启Feign的支持:

@SpringBootApplication
@EnableFeignClients //开启feign支持
public class OpenfeignApplication {

public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}
}

定义HelloService 接口,使用OpenFeign:

@FeignClient("provider")
public interface HelloService {

@GetMapping("/hello")
String hello();//这里的方法名随意取 无需遵循java规则
}

最后调用 HelloController 中,调用 HelloService 进行测试:

@RestController
public class HelloController {

@Autowired
HelloService helloService;

@GetMapping("/hello")
public String hello(){
return helloService.hello();
}
}

测试结果

2.参数传递

和普通参数传递的区别:

  • 参数一定要绑定参数名
  • 如果通过Header来传递参数,一定要中文转码

测试的服务端接口,继续使用 provider 的接口;然后,在 openFeign 中添加调用接口即可。

@FeignClient("provider")
public interface HelloService {

@GetMapping("/hello")
String hello();//这里的方法名随意取 无需遵循java规则

@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);// k/v形式要标记参数名称

@PostMapping("/user2")
User addUser(@RequestBody User user);

@DeleteMapping("/user14/{id}")
void deleteUserById(@PathVariable("id") Integer id);

@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name);//放在header之后的参数,一定要转码传递
}

注意:凡是k/v形式的参数,要标记参数名称。

然后分别启动 Eureka、Provider、OpenFeign,访问 http://localhost:5000/hello ,控制台如下:
在这里插入图片描述

3.继承特性

将 provider 和 openfeign 中公共的部分提取出来一起使用。
新建一个Moudule ,叫做 first-api,由于这个模块要被其它模块所依赖,所以这个模块是一个maven项目,还有用springmvc的东西,因此在创建成功后,要添加 web 依赖:

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>commons</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>

然后定义公共接口,就是provider 和 openfeign中公共的部分:

public interface UserService {

@GetMapping("/hello")
String hello();//这里的方法名随意取 无需遵循java规则

@GetMapping("/hello2")
String hello2(@RequestParam("name") String name);// k/v形式要标记参数名称

@PostMapping("/user2")
User addUser2(@RequestBody User user);

@DeleteMapping("/user14/{id}")
void deleteUser2(@PathVariable("id") Integer id);

@GetMapping("/user3")
void getUserByName(@RequestHeader("name") String name) throws UnsupportedEncodingException;//放在header之后的参数,一定要转码传递
}

接着在provider 和 openfeign 中,分别引用该模块:

<dependency>
<groupId>com.example</groupId>
<artifactId>first-api</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>

添加成功后,在 provider 中实现该接口:

@RestController
public class HelloController implements UserService {
@Value("${server.port}")
Integer port;
@GetMapping("/hello")
@Override
public String hello(){
return "hello 你好"+port;
}

@GetMapping("/hello2")
@Override
public String hello2(String name){
return "hello "+name;
}

// key/value形式传递
@PostMapping("/user1")
public User addUser1(User user){
return user;
}

//json传递
@PostMapping("/user2")
@Override
public User addUser2(@RequestBody User user){
return user;
}

@PutMapping("/user11")
public void updateUser1(User user1){
System.out.println(user1);
}

@PutMapping("/user12")
public void updateUser2(@RequestBody User user2){
System.out.println(user2);
}

@DeleteMapping("/user13")
public void deleteUser1(Integer id){
System.out.println(id);
}

@DeleteMapping("/user14/{id}")
@Override
public void deleteUser2(@PathVariable Integer id){
System.out.println(id);
}

@GetMapping("/user3")
@Override
public void getUserByName(@RequestHeader String name) throws UnsupportedEncodingException {
System.out.println(URLDecoder.decode(name,"UTF-8"));
}
}

在 openfeign 中,定义接口继承自公共接口:

@FeignClient("provider")
public interface HelloService entends UserService{
}

接下来,测试代码不变。
小结:

  1. 使用继承特性,代码简洁不易出错。服务端和消费端的代码统一,一改俱改,不易出错。这样会提高服务端和消费端的耦合度;
  2. 上面所讲的参数传递,在使用了继承之后,依然不变,参数该怎么传还是怎么传。

4.日志

加日志,查看请求调用过程,级别共分四种:

  1. NONE:不开启日志,默认就是这个
  2. BASIC:记录请求方法、URL、响应状态码、执行时间
  3. HEADERS:在BASIC 的基础上,加载请求/响应头
  4. FULL:在HEADERS基础上,再增加body 以及请求元数据

四种级别,可以通过Bean 来配置:

@SpringBootApplication
@EnableFeignClients //开启feign支持
public class OpenfeignApplication {

public static void main(String[] args) {
SpringApplication.run(OpenfeignApplication.class, args);
}

@Bean
Logger.Level loggerLevel(){
return Logger.Level.FULL;
}
}

在 application.properties 中开启日志级别:

logging.level.com.example.openfeign=debug

重启openfeign ,观察效果。

5.数据压缩

# 开启请求的数据压缩
feign.compression.request.enabled=true
# 开启响应的数据压缩
feign.compression.response.enabled=true
# 压缩的数据类型
feign.compression.request.mime-types=text/html,application/json
# 压缩的数据下限,2048 表示当要传输的数据大于 2048 时,才会进行数据压缩
feign.compression.request.min-request-size=2048

6.OpenFeign+Hystrix

6.1 OpenFeig实际上就是对之前请求的一个封装,所以也需要服务降级等功能,首先定义服务降级的方法:

@Component
@RequestMapping("/java")//防止请求地址重复
public class HelloServiceFallback implements HelloService {

@Override
public String hello() {
return "error";
}

@Override
public String hello2(String name) {
return "error2";
}

@Override
public User addUser2(User user) {
return null;
}

@Override
public void deleteUser2(Integer id) {

}

@Override
public void getUserByName(String name) throws UnsupportedEncodingException {

}
}

然后在 HelloService 配置服务降级类:

@FeignClient(value = "provider",fallback = HelloService.class)
public interface HelloService extends UserService {
}

在 application.properties 中开启 Hystrix.

#开启Hystrix
feign.hystrix.enabled=true

6.2 也可以通过自定义 FallbackFactory 来实现服务降级:

@Component
public class HelloServiceFallbackFactory implements FallbackFactory<HelloService> {
@Override
public HelloService create(Throwable throwable) {
return new HelloService() {
@Override
public String hello() {
return "error1--";
}

@Override
public String hello2(String name) {
return "error2--";
}

@Override
public User addUser2(User user) {
return null;
}

@Override
public void deleteUser2(Integer id) {

}

@Override
public void getUserByName(String name) throws UnsupportedEncodingException {

}
};
}
}

HelloService 中配置如下:

@FeignClient(value = "provider",fallbackFactory = HelloServiceFallbackFactory.class)
public interface HelloService extends UserService {
}

访问如下:
在这里插入图片描述
小结:

  1. openfeign+hystrix降级操作 在yml中开启hystrix 在@FeignClient中实现fallbackFactory属性(需要implement FallbackFactory)
  2. 或fallback(实现接口implement Hello1Service添加@RequestMapping(“/xx”)作为区分)
  3. 两种实现方式都要添加@Component注解

项目地址https://github.com/astronger/springcloud-simple-samples